#include "oled.h"
#include "cmsis_os2.h"
#include "oledfont.h"
#include "unistd.h"
#include <hi_i2c.h>
#include <hi_io.h>
#include <iot_errno.h>
#include <iot_gpio.h>
#include <iot_i2c.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define OLED_SCREEN_HIGHT 32

#define OLED_HIGH (OLED_SCREEN_HIGHT / 8)
#define OLED_WIDTH 128

;
#pragma pack(push, 1)
static struct
{
    uint8_t head;
    uint8_t GRAM[OLED_HIGH][OLED_WIDTH];
} GRAM_DATA_PACK = {0x40, {0}};
#pragma pack(pop)

static uint8_t *pos;
// static osMutexId_t mutex;
static int has_edit;

////初始化命令
uint8_t CMD_Data[] = {
    0x00,                        // MEM ADDR
    0xAE,                        // 关闭显示
    0x00,                        // 设置显示时的起始列地址低四位。0
    0x10,                        // 设置显示时的起始列地址高四位。0
    0x40,                        // 设置显示RAM显示起始行寄存器
    0xB0,                        // 用于设置页地址，其低三位的值对应着GRAM的页地址。
    0x81, 0x8f,                  // 设置对比度255
    0xA1,                        // 列地址127被映射到SEG0
    0xA6,                        // Set Normal/Inverse Display
    0xA8, OLED_SCREEN_HIGHT - 1, // 设置多路复用率
    0xC8,                        // 重新映射模式。扫描的COM COM0 (n - 1)
    0xD3, 0x00,                  // Set Display Offset, 0
    0xD5, 0x80,                  // Set Display Clock Divide Ratio/Oscillator Frequency
    0xD9, 0xF1,                  // Set Pre-charge Period
    0xDA, 0x02,                  // Set COM Pins Hardware Configuration
    0x8D, 0x14,                  // DCDC ON
    0x20, 0x00,                  // 设置寻址模式为水平模式
    0xb0,                        // 设置行列指针位置0,0
    0x00, 0x10,
    0xAF, //开启显示
};

//向缓存写数据
static inline void OLED_WR_DATA(uint8_t data)
{
    if (pos <= &GRAM_DATA_PACK.GRAM[OLED_HIGH - 1][OLED_WIDTH - 1])
    {
        *pos++ = data;
        has_edit = 1;
    }
}

static inline void OLED_Set_Pos(uint8_t x, uint8_t y)
{
    if (y < OLED_HIGH && x < OLED_WIDTH)
    {
        pos = GRAM_DATA_PACK.GRAM[y] + x;
    }
}

void OLED_FlushTask(void *arg)
{
    (void) arg;
    OLED_Clear();
    for (;;)
    {
        usleep(30e3);
        if (has_edit)
        {
            OLED_flush(1);
        }
    }
}

//初始化oled屏幕
void OLED_Init(void)
{
    // osMutexAttr_t mutex_attr;
    // mutex_attr.name = "oled mutex";
    // mutex = osMutexNew(&mutex_attr);

    hi_io_set_driver_strength(HI_IO_NAME_GPIO_9, HI_IO_DRIVER_STRENGTH_0);
    hi_io_set_driver_strength(HI_IO_NAME_GPIO_10, HI_IO_DRIVER_STRENGTH_0);
    hi_io_set_func(HI_IO_NAME_GPIO_9, HI_IO_FUNC_GPIO_9_I2C0_SCL);
    hi_io_set_func(HI_IO_NAME_GPIO_10, HI_IO_FUNC_GPIO_10_I2C0_SDA);
    IoTI2cInit(HI_I2C_IDX_0, 400000);
    usleep(200e3);
    IoTI2cWrite(HI_I2C_IDX_0, 0x78, CMD_Data, sizeof(CMD_Data));

    // osThreadAttr_t oled_task_attr;
    // oled_task_attr.name = "OLEDTask";
    // oled_task_attr.stack_size = 128;
    // oled_task_attr.priority = osPriorityBelowNormal;
    // osThreadNew(OLED_FlushTask, NULL, &oled_task_attr);
}

uint8_t OLED_flush(uint32_t timeout)
{
    (void) timeout;
    // osMutexAcquire(mutex, osWaitForever);
    IoTI2cWrite(HI_I2C_IDX_0, 0x78, (uint8_t *)&GRAM_DATA_PACK, sizeof(GRAM_DATA_PACK));
    has_edit = 0;
    // osMutexRelease(mutex);

    return 0;
}

//清屏
void OLED_Clear(void)
{
    // osMutexAcquire(mutex, osWaitForever);
    memset(&GRAM_DATA_PACK.GRAM, 0, sizeof(GRAM_DATA_PACK.GRAM));
    has_edit = 1;
    // osMutexRelease(mutex);
}

void OLED_Enable(int status)
{
    static uint8_t cmd[2] = {0x00, 0xAF};
    uint8_t tmp = status ? 0xAF : 0xAE;
    if (cmd[1] != tmp)
    {
        cmd[1] = tmp;
        IoTI2cWrite(HI_I2C_IDX_0, 0x78, cmd, sizeof(cmd));
    }
}

int32_t oled_pow(uint8_t m, uint8_t n)
{
    uint32_t result = 1;
    while (n--)
        result *= m;
    return result;
}

//显示2个数字
// x,y :起点坐标
// len :数字的位数
// size:字体大小
void OLED_ShowNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size2)
{
    uint8_t t, temp;
    uint8_t enshow = 0;
    for (t = 0; t < len; t++)
    {
        temp = (num / oled_pow(10, len - t - 1)) % 10;
        if (enshow == 0 && t < (len - 1))
        {
            if (temp == 0)
            {
                OLED_ShowChar(x + (size2 == 16 ? 8 : 6) * t, y, ' ', size2);
                continue;
            }
            else
                enshow = 1;
        }
        OLED_ShowChar(x + (size2 == 16 ? 8 : 6) * t, y, temp + '0', size2);
    }
}

void OLED_ShowNum_WithZero(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size2)
{
    uint8_t t, temp;
    uint8_t enshow = 0;
    for (t = 0; t < len; t++)
    {
        temp = (num / oled_pow(10, len - t - 1)) % 10;
        if (enshow == 0 && t < (len - 1))
        {
            if (temp == 0)
            {
                OLED_ShowChar(x + (size2 == 16 ? 8 : 6) * t, y, '0', size2);
                continue;
            }
            else
                enshow = 1;
        }
        OLED_ShowChar(x + (size2 == 16 ? 8 : 6) * t, y, temp + '0', size2);
    }
}

//在指定位置显示一个字符,包括部分字符
// x:0~127
// y:0~63
// mode:0,反白显示;1,正常显示
// size:选择字体 16/12
void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr, uint8_t Char_Size)
{
    uint8_t c = 0, i = 0;
    // osMutexAcquire(mutex, osWaitForever);
    c = chr - ' '; //得到偏移后的值
    if (x > 128 - 1)
    {
        x = 0;
        y = y + 2;
    }
    if (Char_Size == 16)
    {
        if (c * 16 + 15 >= sizeof(F8X16))
        {
            return;
        }

        OLED_Set_Pos(x, y);
        for (i = 0; i < 8; i++)
            OLED_WR_DATA(F8X16[c * 16 + i]);
        OLED_Set_Pos(x, y + 1);
        for (i = 0; i < 8; i++)
            OLED_WR_DATA(F8X16[c * 16 + i + 8]);
    }
    else
    {
        if (c >= sizeof(F6x8) / sizeof(F6x8[0]))
        {
            return;
        }
        OLED_Set_Pos(x, y);
        for (i = 0; i < 6; i++)
            OLED_WR_DATA(F6x8[c][i]);
    }
    // osMutexRelease(mutex);
}

//显示一个字符号串
void OLED_ShowString(uint8_t x, uint8_t y, const char *chr, uint8_t Char_Size)
{
    uint8_t j = 0;
    while (chr[j] != '\0')
    {
        OLED_ShowChar(x, y, chr[j], Char_Size);
        x += 8;
        if (x > OLED_WIDTH - 8)
        {
            x = 0;
            y += 2;
        }
        j++;
    }
}
